#include "vfs.h"
#include "archive.h"

// Util
#include "util/mmgr.h"
#include "util/common_macros.h"
#include "util/log.h"

// num
#include "num/crc32.h"

// standard
#include <string>
#include <cassert>
#include <algorithm>

#ifdef _PLATFORM_WII
#include "wii_mmgr.h"
#endif


using namespace vfs;
using namespace util;

VFS::VFS()
{

/*	Archive*		pArch = new Archive();
	pArch->AddFile("cursor.png");
	pArch->AddFile("img/anon_blocker.png");
	pArch->SaveArchive("test.pak");
	
	AddArchive("test.pak", true);
*/
}

VFS::~VFS()
{
	for (unsigned int i=0; i<m_Files.size(); i++)
	{
		UTIL_SAFE_DELETE(m_Files[i]);
	}
}

VFS&		VFS::Instance()
{
	static VFS	ZipInstance;
	return ZipInstance;
}

std::string	VFS::GetPath(const std::string& _File)
{
	return _File;
}

bool	VFS::LoadLocal(const std::string& _Name, LoadedBlock& _Block)
{
	// Check in the root directory
	FileIO	InputFile;
	if (!InputFile.Open(_Name, FileIO::FILE_BINARY|FileIO::FILE_READ|FileIO::FILE_DISC|FileIO::FILE_MEM32))
	{
		_LOG(MSG_ERROR, "Unable to load file " << _Name);
		return false;
	}
	int	Size = InputFile.GetSize();

	_Block.FileName = _Name;
	_Block.Size		= (int)Size;
#ifdef _PLATFORM_WII
	const int MAX_SIZE = 4.1*1024*1024;
	unsigned char*		LoadingCache = (unsigned char*)WiiMMGR_ScratchPad();
	if (Size > MAX_SIZE)
	{
		// blows our scratchpad!
		_LOG(MSG_ERROR, "File " << _Name << " exceeds scratch-pad size! " << (int)Size << " / " << MAX_SIZE);
		return false;	
	}
	
	_Block.DataArray = LoadingCache;
	_Block.ShouldDelete = false;
#else
	_Block.DataArray= new unsigned char[_Block.Size+1];	// Allocate space for it
#endif
	if (Size == 0)
	{
		memset(_Block.DataArray, 0, _Block.Size+1);		// empty file
	} else
	{
		InputFile.Read(_Block.DataArray, _Block.Size);
	}
	return true;
}

static void	StripFilePath(std::string& Filename)
{
	int Index = (int)Filename.find_last_of("/\\");
	if (Index >= 0)
	{
		std::string	NameOnly =  Filename.substr(Index+1, Filename.size()-Index);
		Filename = Filename.substr(0, Index);
		Index =(int) Filename.find_last_of("/\\");
		if (Index >= 0)
		{
			Filename = Filename.substr(Index+1, Filename.size()-Index) + "/" + NameOnly;
		} else
		{
			Filename = Filename + "/" + NameOnly;
		}
	}
	
}


bool	VFS::GetFile(const std::string& _Name, LoadedBlock& _Block)
{
	if (_Name.empty()) return false;
	// strip out the path
	std::string		Name = _Name;
	StripFilePath(Name);
	unsigned long Key = num::CRC32::FastHash(Name.c_str());
	for (unsigned int i=0; i<m_Files.size(); i++)
	{
		if (m_Files[i]->GetFile(Key, _Block))
		{
			_Block.FileName = Name;
			return true;
		}
	}
	if (LoadLocal(_Name, _Block))	// Found in the local space
		return true;

	_LOG(MSG_ERROR, "Couldn't find file " << Name << " in archives");
	return false;
}

Archive*		VFS::AddArchive(const std::string& Name, bool Precache)
{
	Archive*	pZip = new Archive();
	if (pZip->LoadArchive(Name))
	{
		m_Files.push_back(pZip);

		if (Precache)
		{
			pZip->CacheAll();
		}
		return pZip;
	}
// Unable to load it
	UTIL_SAFE_DELETE(pZip);

	return NULL;
}

bool			VFS::FileExists(const std::string& _Name)
{
	std::string		Name = _Name;
	StripFilePath(Name);
	unsigned long Key = num::CRC32::FastHash(Name.c_str());
	for (unsigned int i=0; i<m_Files.size(); i++)
	{
		if (m_Files[i]->FileExists(Name)) return true;
	}
/*	FileIO	InputFile;
	if (!InputFile.Open(_Name, FileIO::FILE_BINARY|FileIO::FILE_READ|FileIO::FILE_DISC|FileIO::FILE_MEM32))
	{
		return false;
	}
	InputFile.Close();
	*/
	return FileIO::FileExists(_Name);
	return true;
}

int			VFS::BuildFileList(std::vector<std::string>& List, const std::string& Path, const std::string& Ext)
{
	int FileC = 0;

	for (unsigned int i=0; i<m_Files.size(); i++)
	{
		Archive* pFile = m_Files[i];
		FileC += pFile->BuildFileList(List, Path, Ext);
	}

	return FileC;
}

std::string		VFS::GetExtension(const std::string& Name)
{
	int	Index = Name.find_last_of(".");
	if (Index < 0) return "";
	std::string	ext = Name.substr(Index+1, 3);
	std::transform(ext.begin(), ext.end(), ext.begin(), tolower);
	return ext;
}
std::string		VFS::StripExtension(const std::string& Name)
{
	int	Index = Name.find_last_of(".");
	if (Index < 0) return "";
	std::string	ext = Name.substr(0, Index);
	std::transform(ext.begin(), ext.end(), ext.begin(), tolower);
	return ext;
}

std::string		VFS::GetBaseName(const std::string& Name)
{
	int	From = Name.find_last_of("/\\");
	int	To = Name.find_last_of(".");

	if (From < 0) From = -1;
	if (To < 0) To = Name.size();
	std::string	Result = Name.substr(From+1, To-From-1);
	return Result;
}
std::string		VFS::GetPathName(const std::string& Name)
{
	int	To = Name.find_last_of("/\\");
	if (To < 0) return "./";
	std::string	Result = Name.substr(0, To);
	return Result;
}

std::string		VFS::GetFilename(const std::string& Name)
{
	int	From = Name.find_last_of("/\\");
	int	To = Name.size();

	if (From < 0) From = -1;
	if (To < 0) To = Name.size();
	std::string	Result = Name.substr(From+1, To-From-1);
	return Result;
}